/***************************************************************************
 *   Copyright (C) 2006 by Marco Martin                                    *
 *   notmart@gmail.com                                                     *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the Lesser GNU General Public                   *
 *   License as published by the Free Software Foundation;                 *
 *   either version 2 of the License, or (at your option)                  *
 *   any later version.                                                    *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "tastylistview.h"
#include "misc.h"

#include <kdeversion.h>
#include <kstringhandler.h>
#include <qimage.h>
#include <qpainter.h>
#include <qheader.h>
#include <klocale.h>
#include <kurldrag.h>
#include <qapplication.h>


TastyListView::TastyListView( QWidget * parent, const char * name)
  : KListView(parent, name), highLightGroups(true), easyOpen(true)
{

  onItemTimer = new QTimer(this, "onItemTimer");
  underCursorItem = openItem = NULL;
  mouseDown = false;
  actionIconSize = 16;
  actionIconSpace = 32;

  listItemTip = new TastyListViewToolTip(viewport(), this);

  connect(this, SIGNAL(onItem(QListViewItem *) ), SLOT(slotOnItem(QListViewItem *) ) );
  connect(onItemTimer, SIGNAL(timeout()), this, SLOT(slotTimeout()) );
}


TastyListView::~TastyListView()
{
}

void TastyListView::startDrag()
{
  if( !currentItem() )
    return;
  TastyListViewItem *item = dynamic_cast<TastyListViewItem *>(currentItem());
  if( !item )
    return;

  QDragObject *d = new KURLDrag( KURL(item->getDeskopEntryPath()) , viewport() );
  if(!d)
    return;

  if (d->drag() && d->target() != viewport())
    emit moved();
}

void TastyListView::contentsMouseReleaseEvent( QMouseEvent * e  )
{
  int x = e->x();
  if( x > width() || x < 0)
    return;

  if( !currentItem() )
    return;
  TastyListViewItem *item = dynamic_cast<TastyListViewItem *>(currentItem());
  if( !item )
    return;

  if( e->button() == RightButton )
    emit(contextMenuRequested( currentItem(), e->globalPos(), 0) );
  else
    emit(activated( currentItem(), QPoint(x, e->y()), 0) );

  if(item && (item->getType() == TastyListViewItem::ServiceGroup))
  {
    if( !openItem )
    {
      openItem = currentItem();
      return;
    }

    TastyListViewItem *oldOpenItem = dynamic_cast<TastyListViewItem *>(openItem);
    openItem = currentItem();
    if( !oldOpenItem || !oldOpenItem->listView() )
      return;

    oldOpenItem->repaint();
  }
  KListView::contentsMouseReleaseEvent(e);
}

void TastyListView::contentsMouseMoveEvent( QMouseEvent * e  )
{
  KListView::contentsMouseMoveEvent(e);
  mouseDown = (e->state() & Qt::LeftButton);

  if( itemAt(  contentsToViewport(QPoint(e->x(), e->y()))) == 0 )
    underCursorItem = NULL;
}

void TastyListView::leaveEvent( QEvent * e  )
{
  KListView::leaveEvent( e );
  onItemTimer->stop();
  if( openItem )
    setCurrentItem( openItem );
}


void TastyListView::keyPressEvent( QKeyEvent * e )
{

  switch(e->key())
  {
    case Qt::Key_Enter:
    case Qt::Key_Return:
    case Qt::Key_Space:
    {
      emit(activated(currentItem(), QPoint(0,0), 0));
      if(!currentItem())
        return;
      TastyListViewItem *item = dynamic_cast<TastyListViewItem *>(currentItem());
      if(item && item->getType() == TastyListViewItem::ServiceGroup)
      {
        if( !openItem )
        {
          openItem = currentItem();
          return;
        }
        TastyListViewItem *oldOpenItem = dynamic_cast<TastyListViewItem *>(openItem);
        openItem = currentItem();
        if( !oldOpenItem || !oldOpenItem->listView() )
          return;

        oldOpenItem->repaint();
      }
    }
      break;

    case Qt::Key_Up:
    case Qt::Key_Down:
      KListView::keyPressEvent( e );
      break;

    case Qt::Key_Right:
    {
      if(!currentItem())
        return;
      TastyListViewItem *item = dynamic_cast<TastyListViewItem *>(currentItem());
      if(item && easyOpen && !QApplication::reverseLayout() && item->getType() == TastyListViewItem::ServiceGroup)
      {
        emit( activated( currentItem(), QPoint(0,0), 0));
        if( !openItem )
        {
          openItem = currentItem();
          return;
        }
        TastyListViewItem *oldOpenItem = dynamic_cast<TastyListViewItem *>(openItem);
        openItem = currentItem();
        if( !oldOpenItem || !oldOpenItem->listView() )
          return;

        oldOpenItem->repaint();
      }
      focusNextPrevChild(!QApplication::reverseLayout());
      break;
    }
    case Qt::Key_Left:
    {
      if( !currentItem() )
        return;
      TastyListViewItem *item = dynamic_cast<TastyListViewItem *>(currentItem());
      if(item && easyOpen && QApplication::reverseLayout() && item->getType() == TastyListViewItem::ServiceGroup)
      {
        emit( activated( currentItem(), QPoint(0,0), 0));
        if( !openItem )
        {
          openItem = currentItem();
          return;
        }
        TastyListViewItem *oldOpenItem = dynamic_cast<TastyListViewItem *>(openItem);
        openItem = currentItem();
        if( !oldOpenItem || !oldOpenItem->listView() )
          return;

        oldOpenItem->repaint();
      }
      focusNextPrevChild(QApplication::reverseLayout());
      break;
    }
    case Qt::Key_Tab:
      KListView::keyPressEvent( e );
      break;

    default:
      break;
  }
}


void TastyListView::slotOnItem( QListViewItem * listItem )
{
  if( !listItem || listItem->listView() != this )
    return;

  if( listItem != underCursorItem )
  {
    underCursorItem = listItem;
    setCurrentItem(listItem);
    if(mouseDown)
      onItemTimer->start(250, true);
    else
      onItemTimer->start(1000, true);
  }
}

void TastyListView::slotTimeout(  )
{
  if( !underCursorItem /*|| !openItem*/ )
    return;

  TastyListViewItem *tastyUnderCursorItem = dynamic_cast<TastyListViewItem *>(underCursorItem);

  if( easyOpen && tastyUnderCursorItem &&
      tastyUnderCursorItem->getType() == TastyListViewItem::ServiceGroup )
  {
    emit(activated(underCursorItem, QPoint(underCursorItem->listView()->width()/2,1), 0));
    TastyListViewItem *oldOpenItem = dynamic_cast<TastyListViewItem *>(openItem);
    openItem = currentItem();
    if( !oldOpenItem || !oldOpenItem->listView() )
      return;

    oldOpenItem->repaint();
  }
}

///////////TASTYLISTVIEWTOOLTIP
TastyListViewToolTip::TastyListViewToolTip( QWidget *parent, TastyListView *tListView )
  : QToolTip( parent ), listView( tListView )
{
}

void TastyListViewToolTip::maybeTip( const QPoint &pos )
{
  if( !parentWidget() || !listView || !listView->showToolTips() )
    return;

  TastyListViewItem *item = static_cast<TastyListViewItem *>(listView->itemAt( pos ));
  QPoint contentsPos = listView->viewportToContents( pos );
  if( !item || !listView->columns() )
    return;

  int actionWidth = 0;
  TastyListViewItem::ActionType actionType = item->getActionType();
  if( actionType != TastyListViewItem::NoAction )
    actionWidth = listView->getActionIconSpace();

  int column = listView->header()->sectionAt( contentsPos.x() );


  QRect r = listView->itemRect( item );
  int headerPos = listView->header()->sectionPos( column );
  r.setLeft( headerPos );
  r.setRight( headerPos + listView->header()->sectionSize( column ) );

  int actionLeft = r.right()-actionWidth;
  if( pos.x() >= actionLeft )
  {
    r.setLeft( actionLeft );
    switch( actionType )
    {
      case TastyListViewItem::AddBookMark:
        tip( r, i18n( "Add" )+" \""+item->text( column )+"\" "+i18n( "to your favourite applications" ) );
        return;

      case TastyListViewItem::RemoveBookMark:
        tip( r, i18n( "Remove" )+" \""+item->text( column )+"\" "+i18n( "from your favourite applications" ) );
        return;

      case TastyListViewItem::OpenGroup:
        tip( r, i18n( "Browse" )+" \""+item->text( column )+"\"" );
        return;

      case TastyListViewItem::Expand:
        tip( r, i18n( "Expand" )+" \""+item->text( column )+"\"" );
        return;

      case TastyListViewItem::Collapse:
        tip( r, i18n( "Collapse" )+" \""+item->text( column )+"\"" );
        return;

      default:
        break;
    }
  }
  else if( actionType == TastyListViewItem::OpenGroup && !item->hasEllipsis() )
  {
    tip( r, i18n( "Browse" )+" \""+item->text( column )+"\"" );
    return;
  }

  if( !item->hasEllipsis() )
    return;
  tip( r, item->text( column )+"\n"+item->getSubText() );
}


///////////TASTYLISTVIEWITEM

TastyListViewItem::TastyListViewItem( TastyListView * parent )
 : KListViewItem(parent)
{commonConstructor();}

TastyListViewItem::TastyListViewItem( TastyListViewItem * parent )
 : KListViewItem(parent)
{commonConstructor();}

TastyListViewItem::TastyListViewItem( TastyListView * parent, TastyListViewItem * after, QString label1 )
 : KListViewItem(parent, after, label1)
{commonConstructor();cellText = label1;}

TastyListViewItem::TastyListViewItem( TastyListViewItem * parent, TastyListViewItem * after, QString label1 )
 : KListViewItem(parent, after, label1)
{commonConstructor();cellText = label1;}

TastyListViewItem::TastyListViewItem( TastyListView * parent, QString label1 )
 : KListViewItem(parent, label1)
{commonConstructor();cellText = label1;}

TastyListViewItem::TastyListViewItem( TastyListViewItem * parent, QString label1 )
 : KListViewItem(parent, label1)
{commonConstructor();cellText = label1;}

TastyListViewItem::~TastyListViewItem()
{
}

void TastyListViewItem::commonConstructor()
{

   subText="";cellText="";

   actionType = NoAction;

   actionPix = QPixmap();

   menuId = QString();
   desktopEntryPath = QString();
   path = QString();

   ellipsis = false;
   highLight = false;
   displaySubText = true;

}

void TastyListViewItem::loadPixmap()
{
  QString iconFile = "";
  iconLoader = KGlobal::iconLoader();

  if( !listView() )
    return;

  TastyListView *lv = dynamic_cast<TastyListView *>(listView());
  if( !lv )
    return;

  switch( actionType )
  {
    case AddBookMark:
      actionPix = iconLoader->loadIcon("bookmark_add", KIcon::Small, lv->getActionIconSize());
    break;

    case RemoveBookMark:
      actionPix = iconLoader->loadIcon("remove", KIcon::Small, lv->getActionIconSize());
    break;

    case OpenGroup:
      if( QApplication::reverseLayout() )
        actionPix = iconLoader->loadIcon("1leftarrow", KIcon::Small, lv->getActionIconSize());
      else
        actionPix = iconLoader->loadIcon("1rightarrow", KIcon::Small, lv->getActionIconSize());
    break;

    case Expand:
      actionPix = iconLoader->loadIcon("1downarrow", KIcon::Small, lv->getActionIconSize());
    break;

    case Collapse:
      actionPix = iconLoader->loadIcon("1uparrow", KIcon::Small, lv->getActionIconSize());
    break;

    default:
      return;
  }


  if ( actionPix.height () > lv->getActionIconSize())
  {
    QImage img = actionPix.convertToImage();
    if( !img.isNull() )
    {
      img = img.smoothScale ( lv->getActionIconSize(), lv->getActionIconSize());
      actionPix = QPixmap (img);
    }
  }
}

QString TastyListViewItem::key( int column, bool ascending ) const
{
  ascending = ascending;
  QString prefix;

  //ensure all the categories are before the leaf items
  if( itemType == ServiceGroup )
    prefix = "0";
  else
    prefix = "1";
  return prefix.append(text( column ));
}

void TastyListViewItem::setup ( )
{
  //KListViewItem::setup();

  //calculate listitem height
  QFontMetrics fm( listView()->font() );

  int pixmapHeight = 5;
  if( pixmap(0) )
    pixmapHeight = pixmap(0)->height()+4;

  if( displaySubText && !subText.isEmpty() )
  {
    int textHeight = (int)(fm.height()*2.5);
    setHeight( (pixmapHeight > textHeight) ? pixmapHeight : textHeight );
  }
  else
    setHeight( pixmapHeight );
}


/*Adapted from Amarok's Statistic listView Copyright (c) 2006 Seb Ruiz*/
void TastyListViewItem::paintCell ( QPainter * p, const QColorGroup & cg, int column, int width, int align )
{
  int textHeight = height();
  QString name = cellText;

  int textX = 0;

  QColor fillColor, textColor;

# if KDE_VERSION < KDE_MAKE_VERSION(3,3,91)
#   define BackgroundColor  backgroundColor()
# else
#   define BackgroundColor  backgroundColor(0)
# endif


  fillColor = isSelected() ? cg.highlight() : BackgroundColor;

  textColor = isSelected() ? cg.highlightedText() : cg.text();

  if( !listView() )
    return;
  TastyListView *lv = dynamic_cast<TastyListView *>( listView() );
  if( !lv )
    return;

  QFont font( lv->font() );

  if( !isSelected() && (lv->getOpenItem() == this||
       (lv->getHighLightGroups() && itemType == ServiceGroup)) )
    fillColor = alphaBlendColors( fillColor, cg.highlight(), 200);

  else if( !isSelected() && highLight )
  {
    int hue, saturation, value;
    cg.highlight().getHsv(&hue, &saturation, &value);
    //calculate the inverse color 128 means rotating the spectral value by 180 degrees
    fillColor.setHsv( (hue+128)%256, saturation/2, value );
  }

  else if( isSelected() && !lv->hasFocus() )
    fillColor = alphaBlendColors( fillColor, BackgroundColor, 150);

  QFontMetrics fm( font );
  widthChanged(column);

  QPixmap buffer(width*2, textHeight);

  if( buffer.isNull() )
    return;

  buffer.fill( fillColor );

  QPainter pBuf(&buffer);


  if( pixmap( column ) )
  {
      int y = (textHeight - pixmap(column)->height())/2;
      pBuf.drawPixmap( 0, y, *pixmap(column) );
      textX += pixmap(column)->width() + 4;
  }

    //Calculate the ellipsis for the MAIN text
  int extraSpace = fm.width("...") + textX + lv->getActionIconSpace();
  ellipsis = false;
  while( (fm.width(name)+extraSpace) > width && name.length() > 4)
  {
    name.truncate(name.length()-1);
    ellipsis = true;
  }
  if( ellipsis )
    name.append("...");


  if( name == "separator" )
  {
      int y = textHeight/2;
      pBuf.setPen(cg.background().dark(140));
      pBuf.drawLine(textX, y, width, y);
      pBuf.setPen(textColor);
      pBuf.end();
      p->drawPixmap( 0, 0, buffer );
      return;
  }

  if( fm.width( name ) + textX + lv->itemMargin()*2 > width )
  {
      const int _width = width - textX - lv->itemMargin()*2;
      name = KStringHandler::rPixelSqueeze( name, pBuf.fontMetrics(), _width );
  }

  pBuf.setPen(textColor);
  pBuf.drawText( textX, 3, width, textHeight, AlignTop, name );

  if( displaySubText && !subText.isEmpty() )
  {
    font.setPointSize( max((int)(font.pointSize()/1.2), 7) );
    pBuf.setFont( font );

    QString subTextCopy = subText;
    QFontMetrics sfm( font );

    //Calculate the ellipsis for the subtext
    int extraSpace = fm.width("...") + textX + lv->getActionIconSpace();
    bool ellipsisSubText = false;
    while( (sfm.width(subTextCopy)+extraSpace) > width && subTextCopy.length() > 4)
    {
      subTextCopy.truncate(subTextCopy.length()-1);
      ellipsisSubText = true;
    }
    if( ellipsisSubText )
    {
      subTextCopy.append("...");
      ellipsis = true;
    }

    pBuf.setPen(cg.background().dark(140));
    pBuf.drawLine(textX, fm.height() + 3, width-textX-5, fm.height() + 3);
    pBuf.setPen(textColor.light(130));
    pBuf.drawText( textX, fm.height() + 4, width, fm.height(), AlignTop, subTextCopy );
  }

  if( !actionPix.isNull() &&
       (actionType == OpenGroup ||
        actionType == Expand ||
        actionType == Collapse ||
        lv->currentItem() == this)  )
  {
    int y = (textHeight - actionPix.height())/2;
    pBuf.drawPixmap( width-(actionPix.width()+5), y, actionPix );
  }


  pBuf.end();
  p->drawPixmap( 0, 0, buffer );
}



//EOF
